package nba2kmodtool.filedesc;

import java.awt.GraphicsConfiguration;
import java.awt.image.BufferedImage;

import javax.media.j3d.Alpha;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.Material;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Texture;
import javax.media.j3d.TextureAttributes;
import javax.media.j3d.TextureUnitState;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Color3f;
import javax.vecmath.Color4f;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.universe.PlatformGeometry;
import com.sun.j3d.utils.universe.SimpleUniverse;
import com.sun.j3d.utils.universe.ViewingPlatform;

/**
 * NBA2K Game Model Render
 * @company ZSword (C) Copyright
 * @author JemiZhuu(周士淳)
 * @category N2kModelRender
 * @version 1.0
 * @since 2011-11-16
 */
public class N2kModelRender {

	private boolean spin = true;
	private SimpleUniverse univ = null;
	private BranchGroup space = null;
	private Canvas3D canvas = null;
	private TransformGroup objScale = null;
	private TransformGroup objTrans = null;
	private Background bgNode = null;
	private Alpha rotationAlpha;
	private BranchGroup objScene = null;
	private Appearance appearance = null;
	private TextureUnitState textureUnitState[] = null;

	private void loadScene(BaseIffFile iff, N2k3DModelPart[] parts) {
		N2kModelLoader f = new N2kModelLoader();
		BranchGroup shapes = null;
		try {
			shapes = f.load(iff, parts, appearance);
		} catch (Exception e) {
			e.printStackTrace();
		}
		shapes.setCapability(BranchGroup.ALLOW_DETACH);
		objScene.addChild(shapes);
	}

	private void updateTexture(BufferedImage textpic) {
		if (textpic != null) {
			// create texture
			TextureLoader loader = new TextureLoader(textpic);
			Texture texture = loader.getTexture();//创建纹理对象
			texture.setBoundaryModeS(Texture.WRAP);
			texture.setBoundaryModeT(Texture.WRAP);
			texture.setBoundaryColor(new Color4f(0.0f, 1.0f, 0.0f, 0.0f));
			//create appearance
			TextureAttributes textAttr = new TextureAttributes();
			textAttr.setTextureMode(TextureAttributes.MODULATE);
			textureUnitState[0].setTexture(texture);
		}
	}

	public void loadTexture(BufferedImage textpic) {
		updateTexture(textpic);
	}

	private void createSceneGraph() {
		// Create the root of the branch graph
		this.space = new BranchGroup();
		//Create a Transformgroup to scale all objects so they		
		this.objScale = new TransformGroup();
		Transform3D t3d = new Transform3D();
		t3d.setScale(0.7);
		objScale.setTransform(t3d);
		//Create Appearance
		this.appearance = new Appearance();
		this.textureUnitState = new TextureUnitState[2];
		// load textures
		TextureAttributes texAttr = new TextureAttributes();
		texAttr.setTextureMode(TextureAttributes.MODULATE);

		textureUnitState[0] = new TextureUnitState();
		textureUnitState[0].setCapability(TextureUnitState.ALLOW_STATE_WRITE);

		textureUnitState[1] = new TextureUnitState();
		textureUnitState[1].setCapability(TextureUnitState.ALLOW_STATE_WRITE);

		Material m = new Material();
		appearance.setMaterial(m);
		appearance.setTextureUnitState(textureUnitState);
		// Create the transform group node and initialize it to the
		// identity.  Enable the TRANSFORM_WRITE capability so that
		// our behavior code can modify it at runtime.  Add it to the
		// root of the subgraph.
		this.objTrans = new TransformGroup();
		objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		this.objScene = new BranchGroup();
		objScene.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
		objScene.setCapability(BranchGroup.ALLOW_CHILDREN_READ);
		objScene.setCapability(BranchGroup.ALLOW_CHILDREN_WRITE);
		objScene.setCapability(BranchGroup.ALLOW_PARENT_READ);
		objTrans.addChild(objScene);
		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

		if (spin) {
			Transform3D yAxis = new Transform3D();
			this.rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 4000, 0, 0, 0, 0, 0);

			RotationInterpolator rotator = new RotationInterpolator(rotationAlpha, objTrans, yAxis, 0.0f,
					(float) Math.PI * 2.0f);
			rotator.setSchedulingBounds(bounds);
			objTrans.addChild(rotator);
			objScale.addChild(objTrans);
		}
		space.addChild(objScale);
		// Set up the background
		Color3f bgColor = new Color3f(0.05f, 0.05f, 0.5f);
		this.bgNode = new Background(bgColor);
		bgNode.setApplicationBounds(bounds);
		space.addChild(bgNode);
		objScene.compile();
		space.compile();
	}

	private void createUniverse() {
		// Get the preferred graphics configuration for the default screen
		GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();
		// Create a Canvas3D using the preferred configuration
		this.canvas = new Canvas3D(config);
		// Create simple universe with view branch
		univ = new SimpleUniverse(canvas);
		BoundingSphere bounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);
		// add mouse behaviors to the ViewingPlatform
		ViewingPlatform viewingPlatform = univ.getViewingPlatform();
		PlatformGeometry pg = new PlatformGeometry();
		// Set up the ambient light
		Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
		AmbientLight ambientLightNode = new AmbientLight(ambientColor);
		ambientLightNode.setInfluencingBounds(bounds);
		pg.addChild(ambientLightNode);
		// Set up the directional lights
		Color3f light1Color = new Color3f(1.0f, 0.7f, 0.0f);
		Vector3f light1Direction = new Vector3f(1.0f, 1.0f, 1.0f);
		Color3f light2Color = new Color3f(1.0f, 1.0f, 1.0f);
		Vector3f light2Direction = new Vector3f(-1.0f, -1.0f, -1.0f);
		DirectionalLight light1 = new DirectionalLight(light1Color, light1Direction);
		light1.setInfluencingBounds(bounds);
		pg.addChild(light1);
		DirectionalLight light2 = new DirectionalLight(light2Color, light2Direction);
		light2.setInfluencingBounds(bounds);
		pg.addChild(light2);
		viewingPlatform.setPlatformGeometry(pg);
		// This will move the ViewPlatform back a bit so the
		// objects in the scene can be viewed.
		viewingPlatform.setNominalViewingTransform();
		if (spin) {
			OrbitBehavior orbit = new OrbitBehavior(this.canvas, OrbitBehavior.REVERSE_ALL);
			orbit.setSchedulingBounds(bounds);
			viewingPlatform.setViewPlatformBehavior(orbit);
		}
		// Ensure at least 5 msec per frame (i.e., < 200Hz)
		univ.getViewer().getView().setMinimumFrameCycleTime(5);
		createSceneGraph();
		univ.addBranchGraph(space);
	}

	public void rotationModels(boolean flag) {
		if (rotationAlpha == null) {
			return;
		}
		if (flag) {
			rotationAlpha.resume();
		} else {
			rotationAlpha.pause();
		}
	}

	public void loadModelData(BaseIffFile iff, N2k3DModelPart model, BufferedImage... texture) {
		loadModelDatas(iff, new N2k3DModelPart[] { model });
	}

	public void loadModelDatas(BaseIffFile iff, N2k3DModelPart[] models, BufferedImage... texture) {
		objScene.removeAllChildren();
		loadScene(iff, models);
	}

	public void clearScene() {
		objScene.removeAllChildren();
	}

	public void destroy() {
		if (space != null) {
			univ.getLocale().removeBranchGraph(space);
		}
	}

	public Canvas3D createCanvas() {
		if (univ == null) {
			createUniverse();
		}
		return this.canvas;
	}
}
